The goal of this analysis is to assess the SMP population with CWL monitoring data, categorize them and find the underrepresented smp features in our data set. This is to ensure our monitoring effort results in a more comprehensive data set that covers all types of SMPs with varying design metrics such as loading ratios. The output of this analysis can assist the data collection team with their future site selection process.

#Libraries
      library(odbc)
      library(DBI)
      library(lubridate)
      library(tidyverse)
      library(stats)
      library(gridExtra)
      library(grid)
      library(gtable)
      library(ggtext)
      library(dplyr)
      library(ggplot2)
      library(knitr)
      library(plotly)
      library(cluster)
      library(factoextra)
# DB PG14
    con <- dbConnect(odbc::odbc(), dsn = "mars14_data", uid = Sys.getenv("shiny_uid"), pwd = Sys.getenv("shiny_pwd"), MaxLongVarcharSize = 8190)

#Getting list of SMPs
    cwl_smp <- dbGetQuery(con,"SELECT DISTINCT smp_id
           FROM fieldwork.viw_deployment_full_cwl")
    
#Getting the greenit info
    smpbdv <- dbGetQuery(con,"SELECT * FROM external.tbl_smpbdv")
    sysbdv <- dbGetQuery(con,"SELECT * FROM external.tbl_systembdv")
    greenit_unified <-dbGetQuery(con,"SELECT * FROM external.viw_greenit_unified")

# join the greenit table with our smp list
    smp_features <- cwl_smp %>% 
      inner_join(smpbdv, by="smp_id")
# Unmonitored SMPs
    unmonitored_smp <- read.csv("C:\\Users\\Farshad.Ebrahimi\\OneDrive - City of Philadelphia\\Github Projects\\smp-population-assesment\\unmonitored_sites.csv") %>%
      select(smp_id = SMP.ID) %>%
      distinct()
# unmonitored smp_features
    unmonitored_smp_features <- unmonitored_smp %>%
      inner_join(smpbdv, by="smp_id")

The following code chunk calculates the break-down of SMP types with CWL data.

#number of smp types
smp_type_break <- smp_features %>% 
  group_by(smp_smptype) %>%
  summarise(count = n()) %>%
  select(Type = smp_smptype, count)

#number of unmonitored smps
unmonitored_smp_type_break <- unmonitored_smp_features %>% 
  group_by(smp_smptype) %>%
  summarise(count = n()) %>%
  select(Type = smp_smptype, count)

The following Pie chart shows the break-down of all SMPs (monitored and unmonitored).

#combining all smps (monitored and unmonitored) and get fractions based on type of smp-finally normalize them
total_smp <- unmonitored_smp_type_break %>%
  full_join(smp_type_break, by="Type")
total_smp[is.na(total_smp)] <- 0
total_smp <- total_smp %>%
  mutate(total_number = count.x+count.y) %>%
  select(Type, total_number)
total_smp <- total_smp %>%
    mutate(sum_all = sum(total_number)) %>%
    mutate(percent_all = (total_number/sum_all)*100)

#get the percent of monitored SMP
smp_type_break_fraction <- smp_type_break %>%
    mutate(sum_all = sum(count)) %>%
    mutate(percent_monitored = (count/sum_all)*100)

#normalize the monitored smp by the total smp percents
normalized_fractions <- smp_type_break_fraction %>%
  full_join(total_smp, by="Type") %>%
  mutate(normalized_percent = percent_monitored/percent_all) %>%
  select(Type, percent_monitored, percent_all, normalized_percent)

normalized_fractions[is.na(normalized_fractions)] <- 0

The following table is aimed to identify the over-representing (normalized percent > 1) and under-representing (normalized percent <1) SMP types.

#calculating the normalized percentage of smp types
kable(arrange(normalized_fractions, normalized_percent), caption = "Normalized SMP Break-down")
Normalized SMP Break-down
Type percent_monitored percent_all normalized_percent
Green Roof 0.0000000 0.1490313 0.0000000
Stormwater Tree 0.0000000 4.1728763 0.0000000
Bumpout 2.5862069 8.4947839 0.3044465
Swale 1.2931034 2.5335320 0.5103955
Planter 5.8189655 7.5260805 0.7731734
Infiltration/Storage Trench 26.0775862 29.5081967 0.8837404
Basin 0.2155172 0.2235469 0.9640805
Rain Garden 12.0689655 11.1028316 1.0870169
Tree Trench 50.6465517 35.6929955 1.4189493
Pervious Paving 0.4310345 0.2980626 1.4461207
Drainage Well 0.8620690 0.2980626 2.8922414

The table above shows Tree trenches are over-represented in our CWL data, while green roof, stormwater tree, bumpout, swale and planters are under-represented (due to monitoring limitation, etc).

The following section is focused on establishing the design metrics range for SMPs in the unmonitored SMP list. These design metrics include drainage area, storm size managed and loading ratio.

#rawstormsizemanaged-in , sys_impervda_ft2, and loading ratio metrics gathering

table_all <- unmonitored_smp_features %>%
  left_join(sysbdv, by="system_id") %>%
  mutate(loading_ratio = sys_lrtotalda_ft2 )
#Clustring analysis based on 3 features of drainage area, loading ratio, and storm sized managed

clustered_moniored <- table_all %>%
  select(system_id, sys_impervda_ft2, loading_ratio, sys_rawstormsizemanaged_in) %>%
  na.omit() %>%
  distinct()

#normalze using mean-sd standardization
normalized_cluster <- clustered_moniored
normalized_cluster[,2:4] <- scale(clustered_moniored[,2:4])

#Elbow method shows a subtle elbow at cluster 4-hence 4 clusters is optimum
#number of clusters
fviz_nbclust(normalized_cluster[,2:4], kmeans, method = "wss")


#k-means 
# Cluster using kmeans with five clusters
cluster_solution <- kmeans(normalized_cluster[,2:4], centers = 4)

# Store the cluster assignments back into the clustering data frame object
clustered_moniored$cluster <-factor(cluster_solution$cluster) 

# Look at the distribution of cluster assignments
table(clustered_moniored$cluster)

  1   2   3   4 
 21 279 136  11 
# Group by the cluster assignment and calculate averages
sys_clus_avg <- clustered_moniored %>%
    group_by(cluster) %>%
    summarize_if(is.numeric, mean)

# View the resulting table
kable(sys_clus_avg)
cluster sys_impervda_ft2 loading_ratio sys_rawstormsizemanaged_in
1 17695.05 68.36332 1.491118
2 20363.52 12.75067 1.886491
3 17350.55 23.52892 1.212609
4 62204.82 10.56821 3.843215
ggplot(data = clustered_moniored, aes(x = loading_ratio, y = sys_rawstormsizemanaged_in, color = cluster)) +
    geom_point()

plot_ly(clustered_moniored, x = ~sys_impervda_ft2, y = ~loading_ratio, z = ~sys_rawstormsizemanaged_in) %>%
  add_markers(color = ~cluster)

In order to have a more relevant clustering method that distinguishes between general types of systems (lined vs unlined or bioretention vs bio infiltration), the unmonitored SMPs were devided into broad categories of Bioinfiltration, Bioretention (lined), Bioretention (unlined), Subsurface infiltration, Subsurface slow release (lined), and Subsurface slow release (unlined). This is done by a system-level feature called sys_modelinputcategory. The code chunk below shows a break down of these SMPs. Please note that green roof and permeable pavements are not included due to the low number.

#sys_modelinputcategory categories
sys_cat <- table_all %>%
  select(system_id, sys_modelinputcategory)%>%
  distinct %>%
  group_by(sys_modelinputcategory) %>%
  summarise(count = n())

kable(sys_cat)
sys_modelinputcategory count
Bioinfiltration 124
Bioretention (lined) 29
Bioretention (unlined) 83
Green Roof 2
Permeable Pavement 1
Subsurface infiltration 159
Subsurface slow release (lined) 113
Subsurface slow release (unlined) 113

clustered_Bioinfiltration <- table_all %>%
  filter(sys_modelinputcategory == "Bioinfiltration") %>%
  select(system_id, sys_impervda_ft2, loading_ratio, sys_rawstormsizemanaged_in, sys_modelinputcategory) %>%
  na.omit() %>%
  distinct()

#normalze using mean-sd standardization
normalized_cluster <- clustered_Bioinfiltration
normalized_cluster[,2:4] <- scale(clustered_Bioinfiltration[,2:4])

#Elbow method shows a subtle elbow at cluster 4-hence 4 clusters is optimum
#number of clusters
fviz_nbclust(normalized_cluster[,2:4], kmeans, method = "wss")


#k-means 
# Cluster using kmeans with five clusters
cluster_solution <- kmeans(normalized_cluster[,2:4], centers = 4)

# Store the cluster assignments back into the clustering data frame object
clustered_Bioinfiltration$cluster <-factor(cluster_solution$cluster) 

# Look at the distribution of cluster assignments
table(clustered_Bioinfiltration$cluster)

 1  2  3  4 
17 31 72  4 
# Group by the cluster assignment and calculate averages
sys_clus_avg <- clustered_Bioinfiltration %>%
    group_by(cluster) %>%
    summarize_if(is.numeric, mean)

# View the resulting table
kable(sys_clus_avg)
cluster sys_impervda_ft2 loading_ratio sys_rawstormsizemanaged_in
1 11184.24 40.58019 1.063568
2 51515.00 15.79839 1.752800
3 15718.52 12.39893 1.778769
4 22358.50 10.18006 4.348037

#3 D plot
plot_ly(clustered_Bioinfiltration, x = ~sys_impervda_ft2, y = ~loading_ratio, z = ~sys_rawstormsizemanaged_in) %>%
  add_markers(color = ~cluster)

Bioretention (lined) clustering. Note that loading ratio field is not applicable here.


clustered_Bioreten_lined <- table_all %>%
  filter(sys_modelinputcategory == "Bioretention (lined)") %>%
  select(system_id, sys_impervda_ft2, sys_rawstormsizemanaged_in, sys_modelinputcategory) %>%
  na.omit() %>%
  distinct()

#normalze using mean-sd standardization
normalized_cluster <- clustered_Bioreten_lined
normalized_cluster[,2:3] <- scale(clustered_Bioreten_lined[,2:3])

#Elbow method shows a subtle elbow at cluster 4-hence 4 clusters is optimum
#number of clusters
fviz_nbclust(normalized_cluster[,2:3], kmeans, method = "wss")


#k-means 
# Cluster using kmeans with five clusters
cluster_solution <- kmeans(normalized_cluster[,2:3], centers = 2)

# Store the cluster assignments back into the clustering data frame object
clustered_Bioreten_lined$cluster <-factor(cluster_solution$cluster) 

# Look at the distribution of cluster assignments
table(clustered_Bioreten_lined$cluster)

 1  2 
 5 24 
# Group by the cluster assignment and calculate averages
sys_clus_avg <- clustered_Bioreten_lined %>%
    group_by(cluster) %>%
    summarize_if(is.numeric, mean)

# View the resulting table
kable(sys_clus_avg)
cluster sys_impervda_ft2 sys_rawstormsizemanaged_in
1 73919.40 2.270016
2 18291.83 1.473926

#3 D plot
plot_ly(clustered_Bioreten_lined, x = ~sys_impervda_ft2, y = ~sys_rawstormsizemanaged_in) %>%
  add_markers(color = ~cluster)
Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Bioretention (unlined) clustering.


clustered_Bioreten_unlined <- table_all %>%
  filter(sys_modelinputcategory == "Bioretention (unlined)") %>%
  select(system_id, sys_impervda_ft2, sys_rawstormsizemanaged_in, loading_ratio, sys_modelinputcategory) %>%
  na.omit() %>%
  distinct()

#normalze using mean-sd standardization
normalized_cluster <- clustered_Bioreten_unlined
normalized_cluster[,2:4] <- scale(clustered_Bioreten_unlined[,2:4])

#Elbow method shows a subtle elbow at cluster 4-hence 4 clusters is optimum
#number of clusters
fviz_nbclust(normalized_cluster[,2:4], kmeans, method = "wss")


#k-means 
# Cluster using kmeans with five clusters
cluster_solution <- kmeans(normalized_cluster[,2:4], centers = 3)

# Store the cluster assignments back into the clustering data frame object
clustered_Bioreten_unlined$cluster <-factor(cluster_solution$cluster) 

# Look at the distribution of cluster assignments
table(clustered_Bioreten_unlined$cluster)

 1  2  3 
 9 21 53 
# Group by the cluster assignment and calculate averages
sys_clus_avg <- clustered_Bioreten_unlined %>%
    group_by(cluster) %>%
    summarize_if(is.numeric, mean)

# View the resulting table
kable(sys_clus_avg)
cluster sys_impervda_ft2 sys_rawstormsizemanaged_in loading_ratio
1 7046.333 3.309078 4.263439
2 42812.095 1.525561 20.624402
3 16789.575 1.639162 12.959101

#3 D plot
plot_ly(clustered_Bioreten_unlined, x = ~sys_impervda_ft2, y = ~sys_rawstormsizemanaged_in, z= ~loading_ratio) %>%
  add_markers(color = ~cluster)

clustered_subsurface <- table_all %>%
  filter(sys_modelinputcategory == "Subsurface infiltration") %>%
  select(system_id, sys_impervda_ft2, sys_rawstormsizemanaged_in, loading_ratio, sys_modelinputcategory) %>%
  na.omit() %>%
  distinct()

#normalze using mean-sd standardization
normalized_cluster <- clustered_subsurface
normalized_cluster[,2:4] <- scale(clustered_subsurface[,2:4])

#Elbow method shows a subtle elbow at cluster 4-hence 4 clusters is optimum
#number of clusters
fviz_nbclust(normalized_cluster[,2:4], kmeans, method = "wss")


#k-means 
# Cluster using kmeans with five clusters
cluster_solution <- kmeans(normalized_cluster[,2:4], centers = 2)

# Store the cluster assignments back into the clustering data frame object
clustered_subsurface$cluster <-factor(cluster_solution$cluster) 

# Look at the distribution of cluster assignments
table(clustered_subsurface$cluster)

 1  2 
99 26 
# Group by the cluster assignment and calculate averages
sys_clus_avg <- clustered_subsurface %>%
    group_by(cluster) %>%
    summarize_if(is.numeric, mean)

# View the resulting table
kable(sys_clus_avg)
cluster sys_impervda_ft2 sys_rawstormsizemanaged_in loading_ratio
1 19525.382 1.837962 12.69806
2 7850.692 1.030724 31.54683

#3 D plot
plot_ly(clustered_subsurface, x = ~sys_impervda_ft2, y = ~sys_rawstormsizemanaged_in, z= ~loading_ratio) %>%
  add_markers(color = ~cluster)
Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

clustered_slowrel_lined <- table_all %>%
  filter(sys_modelinputcategory == "Subsurface slow release (lined)") %>%
  select(system_id, sys_impervda_ft2, sys_rawstormsizemanaged_in, sys_modelinputcategory) %>%
  na.omit() %>%
  distinct()

#normalze using mean-sd standardization
normalized_cluster <- clustered_slowrel_lined
normalized_cluster[,2:3] <- scale(clustered_slowrel_lined[,2:3])

#Elbow method shows a subtle elbow at cluster 4-hence 4 clusters is optimum
#number of clusters
fviz_nbclust(normalized_cluster[,2:3], kmeans, method = "wss")


#k-means 
# Cluster using kmeans with five clusters
cluster_solution <- kmeans(normalized_cluster[,2:3], centers = 2)

# Store the cluster assignments back into the clustering data frame object
clustered_slowrel_lined$cluster <-factor(cluster_solution$cluster) 

# Look at the distribution of cluster assignments
table(clustered_slowrel_lined$cluster)

 1  2 
58 55 
# Group by the cluster assignment and calculate averages
sys_clus_avg <- clustered_slowrel_lined %>%
    group_by(cluster) %>%
    summarize_if(is.numeric, mean)

# View the resulting table
kable(sys_clus_avg)
cluster sys_impervda_ft2 sys_rawstormsizemanaged_in
1 13139.92 1.195020
2 20312.98 1.720648

#3 D plot
plot_ly(clustered_slowrel_lined, x = ~sys_impervda_ft2, y = ~sys_rawstormsizemanaged_in) %>%
  add_markers(color = ~cluster)
Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

clustered_slowrel_unlined <- table_all %>%
  filter(sys_modelinputcategory == "Subsurface slow release (unlined)") %>%
  select(system_id, sys_impervda_ft2, sys_rawstormsizemanaged_in, loading_ratio, sys_modelinputcategory) %>%
  na.omit() %>%
  distinct()

#normalze using mean-sd standardization
normalized_cluster <- clustered_slowrel_unlined
normalized_cluster[,2:3] <- scale(clustered_slowrel_unlined[,2:3])

#Elbow method shows a subtle elbow at cluster 4-hence 4 clusters is optimum
#number of clusters
fviz_nbclust(normalized_cluster[,2:3], kmeans, method = "wss")


#k-means 
# Cluster using kmeans with five clusters
cluster_solution <- kmeans(normalized_cluster[,2:3], centers = 2)

# Store the cluster assignments back into the clustering data frame object
clustered_slowrel_unlined$cluster <-factor(cluster_solution$cluster) 

# Look at the distribution of cluster assignments
table(clustered_slowrel_unlined$cluster)

 1  2 
61 52 
# Group by the cluster assignment and calculate averages
sys_clus_avg <- clustered_slowrel_unlined %>%
    group_by(cluster) %>%
    summarize_if(is.numeric, mean)

# View the resulting table
kable(sys_clus_avg)
cluster sys_impervda_ft2 sys_rawstormsizemanaged_in loading_ratio
1 18886.52 1.910538 19.26955
2 16819.60 1.337858 34.30980

#3 D plot
plot_ly(clustered_slowrel_unlined, x = ~sys_impervda_ft2, y = ~sys_rawstormsizemanaged_in, z = ~loading_ratio) %>%
  add_markers(color = ~cluster)
Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Warning in RColorBrewer::brewer.pal(N, "Set2") :
  minimal value for n is 3, returning requested palette with 3 different levels

Upon clustering the systems, the data will be collected in a unified data frame for stratified sampling. The stratified sampling creates a balanced list of systems that are reflective of the size of each smp type and that of the internal clusters. For example, the code below has generated 58 system (~ 10% of total unmonitored systems); the size of each system type is proportional to that of the population, and within each type of systems, a proportional number of systems has been chosen from each cluster e.g., in Bioinfiltration type systems, 7 systems are randomly chosen from cluster 3, 3 systems from cluster 2, and 2 systems from cluster 1.

#binding all sub-type groups into one table
sys_clustered_all <- bind_rows(clustered_Bioinfiltration[,c("system_id","cluster","sys_modelinputcategory")], clustered_Bioreten_lined[,c("system_id","cluster","sys_modelinputcategory")], clustered_Bioreten_unlined[,c("system_id","cluster","sys_modelinputcategory")], clustered_slowrel_lined[,c("system_id","cluster","sys_modelinputcategory")], clustered_slowrel_unlined[,c("system_id","cluster","sys_modelinputcategory")], clustered_subsurface[,c("system_id","cluster","sys_modelinputcategory")])

#sampling code
stratified_sample <- sys_clustered_all %>%
    group_by(sys_modelinputcategory, cluster) %>%
    sample_frac(size=0.1)

kable(stratified_sample)
system_id cluster sys_modelinputcategory
1023-8 1 Bioinfiltration
282-3 1 Bioinfiltration
1281-31 2 Bioinfiltration
1315-6 2 Bioinfiltration
1383-1 2 Bioinfiltration
281-1 3 Bioinfiltration
1138-3 3 Bioinfiltration
1138-5 3 Bioinfiltration
290-2 3 Bioinfiltration
410-2 3 Bioinfiltration
1138-2 3 Bioinfiltration
416-1 3 Bioinfiltration
1007-2 2 Bioretention (lined)
1347-2 2 Bioretention (lined)
641-5 1 Bioretention (unlined)
595-8 2 Bioretention (unlined)
1288-5 2 Bioretention (unlined)
1198-7 3 Bioretention (unlined)
1053-8 3 Bioretention (unlined)
595-4 3 Bioretention (unlined)
1265-9 3 Bioretention (unlined)
162-2 3 Bioretention (unlined)
1298-2 1 Subsurface infiltration
46-1 1 Subsurface infiltration
1288-10 1 Subsurface infiltration
1202-10 1 Subsurface infiltration
1281-41 1 Subsurface infiltration
1051-7 1 Subsurface infiltration
994-4 1 Subsurface infiltration
1010-5 1 Subsurface infiltration
1287-3 1 Subsurface infiltration
280-3 1 Subsurface infiltration
1298-1 2 Subsurface infiltration
441-15 2 Subsurface infiltration
1265-12 2 Subsurface infiltration
525-2 1 Subsurface slow release (lined)
1299-1 1 Subsurface slow release (lined)
1202-6 1 Subsurface slow release (lined)
1202-7 1 Subsurface slow release (lined)
1202-2 1 Subsurface slow release (lined)
1011-4 1 Subsurface slow release (lined)
1279-13 2 Subsurface slow release (lined)
1051-5 2 Subsurface slow release (lined)
1279-2 2 Subsurface slow release (lined)
1129-4 2 Subsurface slow release (lined)
1307-4 2 Subsurface slow release (lined)
1267-2 2 Subsurface slow release (lined)
246-2 1 Subsurface slow release (unlined)
443-9 1 Subsurface slow release (unlined)
578-5 1 Subsurface slow release (unlined)
1139-23 1 Subsurface slow release (unlined)
443-10 1 Subsurface slow release (unlined)
1287-4 1 Subsurface slow release (unlined)
1240-1 2 Subsurface slow release (unlined)
1051-3 2 Subsurface slow release (unlined)
1394-1 2 Subsurface slow release (unlined)
1202-9 2 Subsurface slow release (unlined)
1359-5 2 Subsurface slow release (unlined)
LS0tDQp0aXRsZTogIlNNUCBwb3B1bGF0aW9uIGFzc2VzbWVudCINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KYXV0aG9yOiAiRmFyc2hhZCBFYnJhaGltaSwgSmFuLzMwLzIwMjMiDQotLS0NCg0KVGhlIGdvYWwgb2YgdGhpcyBhbmFseXNpcyBpcyB0byBhc3Nlc3MgdGhlIFNNUCBwb3B1bGF0aW9uIHdpdGggQ1dMIG1vbml0b3JpbmcgZGF0YSwgY2F0ZWdvcml6ZSB0aGVtIGFuZCBmaW5kIHRoZSB1bmRlcnJlcHJlc2VudGVkIHNtcCBmZWF0dXJlcyBpbiBvdXIgZGF0YSBzZXQuIFRoaXMgaXMgdG8gZW5zdXJlIG91ciBtb25pdG9yaW5nIGVmZm9ydCByZXN1bHRzIGluIGEgbW9yZSBjb21wcmVoZW5zaXZlIGRhdGEgc2V0IHRoYXQgY292ZXJzIGFsbCB0eXBlcyBvZiBTTVBzIHdpdGggdmFyeWluZyBkZXNpZ24gbWV0cmljcyBzdWNoIGFzIGxvYWRpbmcgcmF0aW9zLiBUaGUgb3V0cHV0IG9mIHRoaXMgYW5hbHlzaXMgY2FuIGFzc2lzdCB0aGUgZGF0YSBjb2xsZWN0aW9uIHRlYW0gd2l0aCB0aGVpciBmdXR1cmUgc2l0ZSBzZWxlY3Rpb24gcHJvY2Vzcy4NCg0KYGBge3Igc2VjdGlvbiAxLCBMb2FkaW5nIGxpYnJhcmllcyBhbmQgcXVlcnlpbmcgc21wIGRhdGEgfQ0KI0xpYnJhcmllcw0KICAgICAgbGlicmFyeShvZGJjKQ0KICAgICAgbGlicmFyeShEQkkpDQogICAgICBsaWJyYXJ5KGx1YnJpZGF0ZSkNCiAgICAgIGxpYnJhcnkodGlkeXZlcnNlKQ0KICAgICAgbGlicmFyeShzdGF0cykNCiAgICAgIGxpYnJhcnkoZ3JpZEV4dHJhKQ0KICAgICAgbGlicmFyeShncmlkKQ0KICAgICAgbGlicmFyeShndGFibGUpDQogICAgICBsaWJyYXJ5KGdndGV4dCkNCiAgICAgIGxpYnJhcnkoZHBseXIpDQogICAgICBsaWJyYXJ5KGdncGxvdDIpDQogICAgICBsaWJyYXJ5KGtuaXRyKQ0KICAgICAgbGlicmFyeShwbG90bHkpDQogICAgICBsaWJyYXJ5KGNsdXN0ZXIpDQogICAgICBsaWJyYXJ5KGZhY3RvZXh0cmEpDQojIERCIFBHMTQNCiAgICBjb24gPC0gZGJDb25uZWN0KG9kYmM6Om9kYmMoKSwgZHNuID0gIm1hcnMxNF9kYXRhIiwgdWlkID0gU3lzLmdldGVudigic2hpbnlfdWlkIiksIHB3ZCA9IFN5cy5nZXRlbnYoInNoaW55X3B3ZCIpLCBNYXhMb25nVmFyY2hhclNpemUgPSA4MTkwKQ0KDQojR2V0dGluZyBsaXN0IG9mIFNNUHMNCiAgICBjd2xfc21wIDwtIGRiR2V0UXVlcnkoY29uLCJTRUxFQ1QgRElTVElOQ1Qgc21wX2lkDQogICAgICAgICAgIEZST00gZmllbGR3b3JrLnZpd19kZXBsb3ltZW50X2Z1bGxfY3dsIikNCiAgICANCiNHZXR0aW5nIHRoZSBncmVlbml0IGluZm8NCiAgICBzbXBiZHYgPC0gZGJHZXRRdWVyeShjb24sIlNFTEVDVCAqIEZST00gZXh0ZXJuYWwudGJsX3NtcGJkdiIpDQogICAgc3lzYmR2IDwtIGRiR2V0UXVlcnkoY29uLCJTRUxFQ1QgKiBGUk9NIGV4dGVybmFsLnRibF9zeXN0ZW1iZHYiKQ0KICAgIGdyZWVuaXRfdW5pZmllZCA8LWRiR2V0UXVlcnkoY29uLCJTRUxFQ1QgKiBGUk9NIGV4dGVybmFsLnZpd19ncmVlbml0X3VuaWZpZWQiKQ0KDQojIGpvaW4gdGhlIGdyZWVuaXQgdGFibGUgd2l0aCBvdXIgc21wIGxpc3QNCiAgICBzbXBfZmVhdHVyZXMgPC0gY3dsX3NtcCAlPiUgDQogICAgICBpbm5lcl9qb2luKHNtcGJkdiwgYnk9InNtcF9pZCIpDQojIFVubW9uaXRvcmVkIFNNUHMNCiAgICB1bm1vbml0b3JlZF9zbXAgPC0gcmVhZC5jc3YoIkM6XFxVc2Vyc1xcRmFyc2hhZC5FYnJhaGltaVxcT25lRHJpdmUgLSBDaXR5IG9mIFBoaWxhZGVscGhpYVxcR2l0aHViIFByb2plY3RzXFxzbXAtcG9wdWxhdGlvbi1hc3Nlc21lbnRcXHVubW9uaXRvcmVkX3NpdGVzLmNzdiIpICU+JQ0KICAgICAgc2VsZWN0KHNtcF9pZCA9IFNNUC5JRCkgJT4lDQogICAgICBkaXN0aW5jdCgpDQojIHVubW9uaXRvcmVkIHNtcF9mZWF0dXJlcw0KICAgIHVubW9uaXRvcmVkX3NtcF9mZWF0dXJlcyA8LSB1bm1vbml0b3JlZF9zbXAgJT4lDQogICAgICBpbm5lcl9qb2luKHNtcGJkdiwgYnk9InNtcF9pZCIpDQpgYGANCg0KVGhlIGZvbGxvd2luZyBjb2RlIGNodW5rIGNhbGN1bGF0ZXMgdGhlIGJyZWFrLWRvd24gb2YgU01QIHR5cGVzIHdpdGggQ1dMIGRhdGEuDQoNCmBgYHtyIHNlY3Rpb24gMiwgTW9uaXRvcmVkIFNNUCBicmVhay1kb3duIGJhc2VkIG9uIHR5cGV9DQojbnVtYmVyIG9mIHNtcCB0eXBlcw0Kc21wX3R5cGVfYnJlYWsgPC0gc21wX2ZlYXR1cmVzICU+JSANCiAgZ3JvdXBfYnkoc21wX3NtcHR5cGUpICU+JQ0KICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JQ0KICBzZWxlY3QoVHlwZSA9IHNtcF9zbXB0eXBlLCBjb3VudCkNCmBgYA0KDQpgYGB7ciBzZWN0aW9uIDMgcGllIGNoYXJ0IG9mIG1vbml0b3JlZCBTTVBzLCBlY2hvPUZhbHNlfQ0KZ2dwbG90KHNtcF90eXBlX2JyZWFrLCBhZXMoeD0iIiwgeT0gY291bnQsIGZpbGw9VHlwZSkpK2dlb21fYmFyKHdpZHRoID0gMSwgc3RhdCA9ICJpZGVudGl0eSIpK2Nvb3JkX3BvbGFyKCJ5Iiwgc3RhcnQ9MCkNCmBgYA0KDQpgYGB7ciBzZWN0aW9uIDQsIGxvb2tpbmcgYXQgdGhlIHVubW9uaXRvcmVkIFNNUHMgdG8gc2VlIHRoZSBicmVhay1kb3dufQ0KI251bWJlciBvZiB1bm1vbml0b3JlZCBzbXBzDQp1bm1vbml0b3JlZF9zbXBfdHlwZV9icmVhayA8LSB1bm1vbml0b3JlZF9zbXBfZmVhdHVyZXMgJT4lIA0KICBncm91cF9ieShzbXBfc21wdHlwZSkgJT4lDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lDQogIHNlbGVjdChUeXBlID0gc21wX3NtcHR5cGUsIGNvdW50KQ0KYGBgDQoNClRoZSBmb2xsb3dpbmcgUGllIGNoYXJ0IHNob3dzIHRoZSBicmVhay1kb3duIG9mIGFsbCBTTVBzIChtb25pdG9yZWQgYW5kIHVubW9uaXRvcmVkKS4NCg0KYGBge3Igc2V0aW9uIDUgcGllIGNoYXJ0IG9mIHVubW9uaXRvcmVkIFNNUHMsIGVjaG89RkFMU0V9DQpnZ3Bsb3QodW5tb25pdG9yZWRfc21wX3R5cGVfYnJlYWssIGFlcyh4PSIiLCB5PSBjb3VudCwgZmlsbD1UeXBlKSkrZ2VvbV9iYXIod2lkdGggPSAxLCBzdGF0ID0gImlkZW50aXR5IikrY29vcmRfcG9sYXIoInkiLCBzdGFydD0wKQ0KYGBgDQoNCmBgYHtyIHNlY3Rpb24gNiBmdWxsIGpvaW4gdGhlIHVubW9uaXRvcmVkIGFuZCBtb25pdG9yZWQsIGVjaG89RkFMU0V9DQojZnVsbCBqb2luIHRoZSB1bm1vbml0b3JlZCBhbmQgbW9uaXJvcmVkIGFuZCBjcmVhdGUgYSBjb2x1bW4gdG8gc3VtIHRoZW0gdXANCnNtcF9icmVha2R3b25fZnVsbCA8LSBzbXBfdHlwZV9icmVhayAlPiUNCiAgZnVsbF9qb2luKHVubW9uaXRvcmVkX3NtcF90eXBlX2JyZWFrLCBieT0iVHlwZSIpICU+JQ0KICBzZWxlY3QoVHlwZSwgY291bnRfbW9uaXRvcmVkID0gY291bnQueCwgY291bnRfdW5tb25pdG9yZWQgPSBjb3VudC55KQ0KDQojcmVwbGFjZSBuYSB3aXRoIHplcm8NCnNtcF9icmVha2R3b25fZnVsbFtpcy5uYShzbXBfYnJlYWtkd29uX2Z1bGwpXSA8LSAwDQoNCiNtdXRhdGUgdGhlIHRoaXJkIGNvbHVtbg0Kc21wX2JyZWFrZHdvbl9mdWxsIDwtIHNtcF9icmVha2R3b25fZnVsbCAlPiUNCiAgbXV0YXRlKHN1bV9hbGwgPSBjb3VudF9tb25pdG9yZWQgKyBjb3VudF91bm1vbml0b3JlZCkNCg0KI2RhdGEgZnJhbWUgZm9yIHBsb3R0aW5nIChvbmx5IHR3byBjb2x1bW5zKQ0KcGl2b3RfZGYgPC0gc21wX2JyZWFrZHdvbl9mdWxsICU+JQ0KICBzZWxlY3QoVHlwZSwgY291bnRfbW9uaXRvcmVkLCBzdW1fYWxsKQ0KDQpkZl9sb25nZXIgPC0gcGl2b3RfbG9uZ2VyKHBpdm90X2RmLCBjb2xzID0gYygiY291bnRfbW9uaXRvcmVkIiwgInN1bV9hbGwiKSwgbmFtZXNfdG8gPSAiY2F0ZWdvcnkiLCB2YWx1ZXNfdG8gPSAiY291bnQiKQ0KDQojUGxvdA0KZ2dwbG90KGRmX2xvbmdlcixhZXMoeCA9IFR5cGUsIHkgPSBjb3VudCwgZmlsbD0gY2F0ZWdvcnkpKSArDQogICAgICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKCkpKw0KICAgICAgdGhlbWUoYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTYsIGFuZ2xlID0gMzUpLA0KICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEwKSkNCg0KYGBgDQoNCmBgYHtyIHNlY3Rpb24gNywgYWRkaW5nIHRoZSBtb25pdG9yZWQgYW5kIHVubW9uaW9yZWR9DQojY29tYmluaW5nIGFsbCBzbXBzIChtb25pdG9yZWQgYW5kIHVubW9uaXRvcmVkKSBhbmQgZ2V0IGZyYWN0aW9ucyBiYXNlZCBvbiB0eXBlIG9mIHNtcC1maW5hbGx5IG5vcm1hbGl6ZSB0aGVtDQp0b3RhbF9zbXAgPC0gdW5tb25pdG9yZWRfc21wX3R5cGVfYnJlYWsgJT4lDQogIGZ1bGxfam9pbihzbXBfdHlwZV9icmVhaywgYnk9IlR5cGUiKQ0KdG90YWxfc21wW2lzLm5hKHRvdGFsX3NtcCldIDwtIDANCnRvdGFsX3NtcCA8LSB0b3RhbF9zbXAgJT4lDQogIG11dGF0ZSh0b3RhbF9udW1iZXIgPSBjb3VudC54K2NvdW50LnkpICU+JQ0KICBzZWxlY3QoVHlwZSwgdG90YWxfbnVtYmVyKQ0KdG90YWxfc21wIDwtIHRvdGFsX3NtcCAlPiUNCiAgICBtdXRhdGUoc3VtX2FsbCA9IHN1bSh0b3RhbF9udW1iZXIpKSAlPiUNCiAgICBtdXRhdGUocGVyY2VudF9hbGwgPSAodG90YWxfbnVtYmVyL3N1bV9hbGwpKjEwMCkNCg0KI2dldCB0aGUgcGVyY2VudCBvZiBtb25pdG9yZWQgU01QDQpzbXBfdHlwZV9icmVha19mcmFjdGlvbiA8LSBzbXBfdHlwZV9icmVhayAlPiUNCiAgICBtdXRhdGUoc3VtX2FsbCA9IHN1bShjb3VudCkpICU+JQ0KICAgIG11dGF0ZShwZXJjZW50X21vbml0b3JlZCA9IChjb3VudC9zdW1fYWxsKSoxMDApDQoNCiNub3JtYWxpemUgdGhlIG1vbml0b3JlZCBzbXAgYnkgdGhlIHRvdGFsIHNtcCBwZXJjZW50cw0Kbm9ybWFsaXplZF9mcmFjdGlvbnMgPC0gc21wX3R5cGVfYnJlYWtfZnJhY3Rpb24gJT4lDQogIGZ1bGxfam9pbih0b3RhbF9zbXAsIGJ5PSJUeXBlIikgJT4lDQogIG11dGF0ZShub3JtYWxpemVkX3BlcmNlbnQgPSBwZXJjZW50X21vbml0b3JlZC9wZXJjZW50X2FsbCkgJT4lDQogIHNlbGVjdChUeXBlLCBwZXJjZW50X21vbml0b3JlZCwgcGVyY2VudF9hbGwsIG5vcm1hbGl6ZWRfcGVyY2VudCkNCg0Kbm9ybWFsaXplZF9mcmFjdGlvbnNbaXMubmEobm9ybWFsaXplZF9mcmFjdGlvbnMpXSA8LSAwDQpgYGANCg0KVGhlIGZvbGxvd2luZyB0YWJsZSBpcyBhaW1lZCB0byBpZGVudGlmeSB0aGUgb3Zlci1yZXByZXNlbnRpbmcgKG5vcm1hbGl6ZWQgcGVyY2VudCBcPiAxKSBhbmQgdW5kZXItcmVwcmVzZW50aW5nIChub3JtYWxpemVkIHBlcmNlbnQgXDwxKSBTTVAgdHlwZXMuDQoNCmBgYHtyfQ0KI2NhbGN1bGF0aW5nIHRoZSBub3JtYWxpemVkIHBlcmNlbnRhZ2Ugb2Ygc21wIHR5cGVzDQprYWJsZShhcnJhbmdlKG5vcm1hbGl6ZWRfZnJhY3Rpb25zLCBub3JtYWxpemVkX3BlcmNlbnQpLCBjYXB0aW9uID0gIk5vcm1hbGl6ZWQgU01QIEJyZWFrLWRvd24iKQ0KYGBgDQoNClRoZSB0YWJsZSBhYm92ZSBzaG93cyBUcmVlIHRyZW5jaGVzIGFyZSBvdmVyLXJlcHJlc2VudGVkIGluIG91ciBDV0wgZGF0YSwgd2hpbGUgZ3JlZW4gcm9vZiwgc3Rvcm13YXRlciB0cmVlLCBidW1wb3V0LCBzd2FsZSBhbmQgcGxhbnRlcnMgYXJlIHVuZGVyLXJlcHJlc2VudGVkIChkdWUgdG8gbW9uaXRvcmluZyBsaW1pdGF0aW9uLCBldGMpLg0KDQpUaGUgZm9sbG93aW5nIHNlY3Rpb24gaXMgZm9jdXNlZCBvbiBlc3RhYmxpc2hpbmcgdGhlIGRlc2lnbiBtZXRyaWNzIHJhbmdlIGZvciBTTVBzIGluIHRoZSB1bm1vbml0b3JlZCBTTVAgbGlzdC4gVGhlc2UgZGVzaWduIG1ldHJpY3MgaW5jbHVkZSBkcmFpbmFnZSBhcmVhLCBzdG9ybSBzaXplIG1hbmFnZWQgYW5kIGxvYWRpbmcgcmF0aW8uDQoNCmBgYHtyIHNlY3Rpb24gOCwgcmF3c3Rvcm1zaXplbWFuYWdlZC1pbiAsIHN5c19pbXBlcnZkYV9mdDIsIGFuZCBsb2FkaW5nIHJhdGlvIG1ldHJpY3MgZ2F0aGVyaW5nfQ0KI3Jhd3N0b3Jtc2l6ZW1hbmFnZWQtaW4gLCBzeXNfaW1wZXJ2ZGFfZnQyLCBhbmQgbG9hZGluZyByYXRpbyBtZXRyaWNzIGdhdGhlcmluZw0KDQp0YWJsZV9hbGwgPC0gdW5tb25pdG9yZWRfc21wX2ZlYXR1cmVzICU+JQ0KICBsZWZ0X2pvaW4oc3lzYmR2LCBieT0ic3lzdGVtX2lkIikgJT4lDQogIG11dGF0ZShsb2FkaW5nX3JhdGlvID0gc3lzX2xydG90YWxkYV9mdDIgKQ0KYGBgDQoNCmBgYHtyIHNlY3Rpb24gOX0NCiNDbHVzdHJpbmcgYW5hbHlzaXMgYmFzZWQgb24gMyBmZWF0dXJlcyBvZiBkcmFpbmFnZSBhcmVhLCBsb2FkaW5nIHJhdGlvLCBhbmQgc3Rvcm0gc2l6ZWQgbWFuYWdlZA0KDQpjbHVzdGVyZWRfbW9uaW9yZWQgPC0gdGFibGVfYWxsICU+JQ0KICBzZWxlY3Qoc3lzdGVtX2lkLCBzeXNfaW1wZXJ2ZGFfZnQyLCBsb2FkaW5nX3JhdGlvLCBzeXNfcmF3c3Rvcm1zaXplbWFuYWdlZF9pbikgJT4lDQogIG5hLm9taXQoKSAlPiUNCiAgZGlzdGluY3QoKQ0KDQojbm9ybWFsemUgdXNpbmcgbWVhbi1zZCBzdGFuZGFyZGl6YXRpb24NCm5vcm1hbGl6ZWRfY2x1c3RlciA8LSBjbHVzdGVyZWRfbW9uaW9yZWQNCm5vcm1hbGl6ZWRfY2x1c3RlclssMjo0XSA8LSBzY2FsZShjbHVzdGVyZWRfbW9uaW9yZWRbLDI6NF0pDQoNCiNFbGJvdyBtZXRob2Qgc2hvd3MgYSBzdWJ0bGUgZWxib3cgYXQgY2x1c3RlciA0LWhlbmNlIDQgY2x1c3RlcnMgaXMgb3B0aW11bQ0KI251bWJlciBvZiBjbHVzdGVycw0KZnZpel9uYmNsdXN0KG5vcm1hbGl6ZWRfY2x1c3RlclssMjo0XSwga21lYW5zLCBtZXRob2QgPSAid3NzIikNCg0KI2stbWVhbnMgDQojIENsdXN0ZXIgdXNpbmcga21lYW5zIHdpdGggZml2ZSBjbHVzdGVycw0KY2x1c3Rlcl9zb2x1dGlvbiA8LSBrbWVhbnMobm9ybWFsaXplZF9jbHVzdGVyWywyOjRdLCBjZW50ZXJzID0gNCkNCg0KIyBTdG9yZSB0aGUgY2x1c3RlciBhc3NpZ25tZW50cyBiYWNrIGludG8gdGhlIGNsdXN0ZXJpbmcgZGF0YSBmcmFtZSBvYmplY3QNCmNsdXN0ZXJlZF9tb25pb3JlZCRjbHVzdGVyIDwtZmFjdG9yKGNsdXN0ZXJfc29sdXRpb24kY2x1c3RlcikgDQoNCiMgTG9vayBhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGNsdXN0ZXIgYXNzaWdubWVudHMNCnRhYmxlKGNsdXN0ZXJlZF9tb25pb3JlZCRjbHVzdGVyKQ0KYGBgDQoNCmBgYHtyIHNlY3Rpb24gMTAgYW5hbHl6aW5nIHRoZSBjbHVzdGVyIGZlYXR1cmVzfQ0KIyBHcm91cCBieSB0aGUgY2x1c3RlciBhc3NpZ25tZW50IGFuZCBjYWxjdWxhdGUgYXZlcmFnZXMNCnN5c19jbHVzX2F2ZyA8LSBjbHVzdGVyZWRfbW9uaW9yZWQgJT4lDQogICAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lDQogICAgc3VtbWFyaXplX2lmKGlzLm51bWVyaWMsIG1lYW4pDQoNCiMgVmlldyB0aGUgcmVzdWx0aW5nIHRhYmxlDQprYWJsZShzeXNfY2x1c19hdmcpDQpgYGANCg0KYGBge3IgMkQgUGxvdH0NCmdncGxvdChkYXRhID0gY2x1c3RlcmVkX21vbmlvcmVkLCBhZXMoeCA9IGxvYWRpbmdfcmF0aW8sIHkgPSBzeXNfcmF3c3Rvcm1zaXplbWFuYWdlZF9pbiwgY29sb3IgPSBjbHVzdGVyKSkgKw0KICAgIGdlb21fcG9pbnQoKQ0KYGBgDQoNCmBgYHtyIDNEIHBsb3R9DQpwbG90X2x5KGNsdXN0ZXJlZF9tb25pb3JlZCwgeCA9IH5zeXNfaW1wZXJ2ZGFfZnQyLCB5ID0gfmxvYWRpbmdfcmF0aW8sIHogPSB+c3lzX3Jhd3N0b3Jtc2l6ZW1hbmFnZWRfaW4pICU+JQ0KICBhZGRfbWFya2Vycyhjb2xvciA9IH5jbHVzdGVyKQ0KYGBgDQoNCkluIG9yZGVyIHRvIGhhdmUgYSBtb3JlIHJlbGV2YW50IGNsdXN0ZXJpbmcgbWV0aG9kIHRoYXQgZGlzdGluZ3Vpc2hlcyBiZXR3ZWVuIGdlbmVyYWwgdHlwZXMgb2Ygc3lzdGVtcyAobGluZWQgdnMgdW5saW5lZCBvciBiaW9yZXRlbnRpb24gdnMgYmlvIGluZmlsdHJhdGlvbiksIHRoZSB1bm1vbml0b3JlZCBTTVBzIHdlcmUgZGV2aWRlZCBpbnRvIGJyb2FkIGNhdGVnb3JpZXMgb2YgQmlvaW5maWx0cmF0aW9uLCBCaW9yZXRlbnRpb24gKGxpbmVkKSwgQmlvcmV0ZW50aW9uICh1bmxpbmVkKSwgU3Vic3VyZmFjZSBpbmZpbHRyYXRpb24sIFN1YnN1cmZhY2Ugc2xvdyByZWxlYXNlIChsaW5lZCksIGFuZCBTdWJzdXJmYWNlIHNsb3cgcmVsZWFzZSAodW5saW5lZCkuIFRoaXMgaXMgZG9uZSBieSBhIHN5c3RlbS1sZXZlbCBmZWF0dXJlIGNhbGxlZCBzeXNfbW9kZWxpbnB1dGNhdGVnb3J5LiBUaGUgY29kZSBjaHVuayBiZWxvdyBzaG93cyBhIGJyZWFrIGRvd24gb2YgdGhlc2UgU01Qcy4gUGxlYXNlIG5vdGUgdGhhdCBncmVlbiByb29mIGFuZCBwZXJtZWFibGUgcGF2ZW1lbnRzIGFyZSBub3QgaW5jbHVkZWQgZHVlIHRvIHRoZSBsb3cgbnVtYmVyLg0KDQpgYGB7ciBzZWN0aW9uIDExIHN5c19tb2RlbGlucHV0Y2F0ZWdvcnkgaXMgdXNlZCB0byBjYXRlZ29yaXplIHN5c3RlbXN9DQojc3lzX21vZGVsaW5wdXRjYXRlZ29yeSBjYXRlZ29yaWVzDQpzeXNfY2F0IDwtIHRhYmxlX2FsbCAlPiUNCiAgc2VsZWN0KHN5c3RlbV9pZCwgc3lzX21vZGVsaW5wdXRjYXRlZ29yeSklPiUNCiAgZGlzdGluY3QgJT4lDQogIGdyb3VwX2J5KHN5c19tb2RlbGlucHV0Y2F0ZWdvcnkpICU+JQ0KICBzdW1tYXJpc2UoY291bnQgPSBuKCkpDQoNCmthYmxlKHN5c19jYXQpDQpgYGANCg0KYGBge3Igc2VjdGlvbiAxMiBjbHVzdGVyaW5nIEJpb2luZmlsdHJhdGlvbiB0eXBlIHN5c3RlbXMgfQ0KDQpjbHVzdGVyZWRfQmlvaW5maWx0cmF0aW9uIDwtIHRhYmxlX2FsbCAlPiUNCiAgZmlsdGVyKHN5c19tb2RlbGlucHV0Y2F0ZWdvcnkgPT0gIkJpb2luZmlsdHJhdGlvbiIpICU+JQ0KICBzZWxlY3Qoc3lzdGVtX2lkLCBzeXNfaW1wZXJ2ZGFfZnQyLCBsb2FkaW5nX3JhdGlvLCBzeXNfcmF3c3Rvcm1zaXplbWFuYWdlZF9pbiwgc3lzX21vZGVsaW5wdXRjYXRlZ29yeSkgJT4lDQogIG5hLm9taXQoKSAlPiUNCiAgZGlzdGluY3QoKQ0KDQojbm9ybWFsemUgdXNpbmcgbWVhbi1zZCBzdGFuZGFyZGl6YXRpb24NCm5vcm1hbGl6ZWRfY2x1c3RlciA8LSBjbHVzdGVyZWRfQmlvaW5maWx0cmF0aW9uDQpub3JtYWxpemVkX2NsdXN0ZXJbLDI6NF0gPC0gc2NhbGUoY2x1c3RlcmVkX0Jpb2luZmlsdHJhdGlvblssMjo0XSkNCg0KI0VsYm93IG1ldGhvZCBzaG93cyBhIHN1YnRsZSBlbGJvdyBhdCBjbHVzdGVyIDQtaGVuY2UgNCBjbHVzdGVycyBpcyBvcHRpbXVtDQojbnVtYmVyIG9mIGNsdXN0ZXJzDQpmdml6X25iY2x1c3Qobm9ybWFsaXplZF9jbHVzdGVyWywyOjRdLCBrbWVhbnMsIG1ldGhvZCA9ICJ3c3MiKQ0KDQojay1tZWFucyANCiMgQ2x1c3RlciB1c2luZyBrbWVhbnMgd2l0aCBmaXZlIGNsdXN0ZXJzDQpjbHVzdGVyX3NvbHV0aW9uIDwtIGttZWFucyhub3JtYWxpemVkX2NsdXN0ZXJbLDI6NF0sIGNlbnRlcnMgPSA0KQ0KDQojIFN0b3JlIHRoZSBjbHVzdGVyIGFzc2lnbm1lbnRzIGJhY2sgaW50byB0aGUgY2x1c3RlcmluZyBkYXRhIGZyYW1lIG9iamVjdA0KY2x1c3RlcmVkX0Jpb2luZmlsdHJhdGlvbiRjbHVzdGVyIDwtZmFjdG9yKGNsdXN0ZXJfc29sdXRpb24kY2x1c3RlcikgDQoNCiMgTG9vayBhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGNsdXN0ZXIgYXNzaWdubWVudHMNCnRhYmxlKGNsdXN0ZXJlZF9CaW9pbmZpbHRyYXRpb24kY2x1c3RlcikNCg0KIyBHcm91cCBieSB0aGUgY2x1c3RlciBhc3NpZ25tZW50IGFuZCBjYWxjdWxhdGUgYXZlcmFnZXMNCnN5c19jbHVzX2F2ZyA8LSBjbHVzdGVyZWRfQmlvaW5maWx0cmF0aW9uICU+JQ0KICAgIGdyb3VwX2J5KGNsdXN0ZXIpICU+JQ0KICAgIHN1bW1hcml6ZV9pZihpcy5udW1lcmljLCBtZWFuKQ0KDQojIFZpZXcgdGhlIHJlc3VsdGluZyB0YWJsZQ0Ka2FibGUoc3lzX2NsdXNfYXZnKQ0KDQojMyBEIHBsb3QNCnBsb3RfbHkoY2x1c3RlcmVkX0Jpb2luZmlsdHJhdGlvbiwgeCA9IH5zeXNfaW1wZXJ2ZGFfZnQyLCB5ID0gfmxvYWRpbmdfcmF0aW8sIHogPSB+c3lzX3Jhd3N0b3Jtc2l6ZW1hbmFnZWRfaW4pICU+JQ0KICBhZGRfbWFya2Vycyhjb2xvciA9IH5jbHVzdGVyKQ0KYGBgDQoNCkJpb3JldGVudGlvbiAobGluZWQpIGNsdXN0ZXJpbmcuIE5vdGUgdGhhdCBsb2FkaW5nIHJhdGlvIGZpZWxkIGlzIG5vdCBhcHBsaWNhYmxlIGhlcmUuDQoNCmBgYHtyIHNlY3Rpb24gMTMgQmlvcmV0ZW50aW9uIGxpbmVkIGNsdXN0ZXJpbmd9DQoNCmNsdXN0ZXJlZF9CaW9yZXRlbl9saW5lZCA8LSB0YWJsZV9hbGwgJT4lDQogIGZpbHRlcihzeXNfbW9kZWxpbnB1dGNhdGVnb3J5ID09ICJCaW9yZXRlbnRpb24gKGxpbmVkKSIpICU+JQ0KICBzZWxlY3Qoc3lzdGVtX2lkLCBzeXNfaW1wZXJ2ZGFfZnQyLCBzeXNfcmF3c3Rvcm1zaXplbWFuYWdlZF9pbiwgc3lzX21vZGVsaW5wdXRjYXRlZ29yeSkgJT4lDQogIG5hLm9taXQoKSAlPiUNCiAgZGlzdGluY3QoKQ0KDQojbm9ybWFsemUgdXNpbmcgbWVhbi1zZCBzdGFuZGFyZGl6YXRpb24NCm5vcm1hbGl6ZWRfY2x1c3RlciA8LSBjbHVzdGVyZWRfQmlvcmV0ZW5fbGluZWQNCm5vcm1hbGl6ZWRfY2x1c3RlclssMjozXSA8LSBzY2FsZShjbHVzdGVyZWRfQmlvcmV0ZW5fbGluZWRbLDI6M10pDQoNCiNFbGJvdyBtZXRob2Qgc2hvd3MgYSBzdWJ0bGUgZWxib3cgYXQgY2x1c3RlciA0LWhlbmNlIDQgY2x1c3RlcnMgaXMgb3B0aW11bQ0KI251bWJlciBvZiBjbHVzdGVycw0KZnZpel9uYmNsdXN0KG5vcm1hbGl6ZWRfY2x1c3RlclssMjozXSwga21lYW5zLCBtZXRob2QgPSAid3NzIikNCg0KI2stbWVhbnMgDQojIENsdXN0ZXIgdXNpbmcga21lYW5zIHdpdGggZml2ZSBjbHVzdGVycw0KY2x1c3Rlcl9zb2x1dGlvbiA8LSBrbWVhbnMobm9ybWFsaXplZF9jbHVzdGVyWywyOjNdLCBjZW50ZXJzID0gMikNCg0KIyBTdG9yZSB0aGUgY2x1c3RlciBhc3NpZ25tZW50cyBiYWNrIGludG8gdGhlIGNsdXN0ZXJpbmcgZGF0YSBmcmFtZSBvYmplY3QNCmNsdXN0ZXJlZF9CaW9yZXRlbl9saW5lZCRjbHVzdGVyIDwtZmFjdG9yKGNsdXN0ZXJfc29sdXRpb24kY2x1c3RlcikgDQoNCiMgTG9vayBhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGNsdXN0ZXIgYXNzaWdubWVudHMNCnRhYmxlKGNsdXN0ZXJlZF9CaW9yZXRlbl9saW5lZCRjbHVzdGVyKQ0KDQojIEdyb3VwIGJ5IHRoZSBjbHVzdGVyIGFzc2lnbm1lbnQgYW5kIGNhbGN1bGF0ZSBhdmVyYWdlcw0Kc3lzX2NsdXNfYXZnIDwtIGNsdXN0ZXJlZF9CaW9yZXRlbl9saW5lZCAlPiUNCiAgICBncm91cF9ieShjbHVzdGVyKSAlPiUNCiAgICBzdW1tYXJpemVfaWYoaXMubnVtZXJpYywgbWVhbikNCg0KIyBWaWV3IHRoZSByZXN1bHRpbmcgdGFibGUNCmthYmxlKHN5c19jbHVzX2F2ZykNCg0KIzMgRCBwbG90DQpwbG90X2x5KGNsdXN0ZXJlZF9CaW9yZXRlbl9saW5lZCwgeCA9IH5zeXNfaW1wZXJ2ZGFfZnQyLCB5ID0gfnN5c19yYXdzdG9ybXNpemVtYW5hZ2VkX2luKSAlPiUNCiAgYWRkX21hcmtlcnMoY29sb3IgPSB+Y2x1c3RlcikNCmBgYA0KDQpCaW9yZXRlbnRpb24gKHVubGluZWQpIGNsdXN0ZXJpbmcuDQoNCmBgYHtyIHNlY3Rpb24gMTQgQmlvcmV0ZW50aW9uIHVubGluZWQgY2x1c3RlcmluZ30NCg0KY2x1c3RlcmVkX0Jpb3JldGVuX3VubGluZWQgPC0gdGFibGVfYWxsICU+JQ0KICBmaWx0ZXIoc3lzX21vZGVsaW5wdXRjYXRlZ29yeSA9PSAiQmlvcmV0ZW50aW9uICh1bmxpbmVkKSIpICU+JQ0KICBzZWxlY3Qoc3lzdGVtX2lkLCBzeXNfaW1wZXJ2ZGFfZnQyLCBzeXNfcmF3c3Rvcm1zaXplbWFuYWdlZF9pbiwgbG9hZGluZ19yYXRpbywgc3lzX21vZGVsaW5wdXRjYXRlZ29yeSkgJT4lDQogIG5hLm9taXQoKSAlPiUNCiAgZGlzdGluY3QoKQ0KDQojbm9ybWFsemUgdXNpbmcgbWVhbi1zZCBzdGFuZGFyZGl6YXRpb24NCm5vcm1hbGl6ZWRfY2x1c3RlciA8LSBjbHVzdGVyZWRfQmlvcmV0ZW5fdW5saW5lZA0Kbm9ybWFsaXplZF9jbHVzdGVyWywyOjRdIDwtIHNjYWxlKGNsdXN0ZXJlZF9CaW9yZXRlbl91bmxpbmVkWywyOjRdKQ0KDQojRWxib3cgbWV0aG9kIHNob3dzIGEgc3VidGxlIGVsYm93IGF0IGNsdXN0ZXIgNC1oZW5jZSA0IGNsdXN0ZXJzIGlzIG9wdGltdW0NCiNudW1iZXIgb2YgY2x1c3RlcnMNCmZ2aXpfbmJjbHVzdChub3JtYWxpemVkX2NsdXN0ZXJbLDI6NF0sIGttZWFucywgbWV0aG9kID0gIndzcyIpDQoNCiNrLW1lYW5zIA0KIyBDbHVzdGVyIHVzaW5nIGttZWFucyB3aXRoIGZpdmUgY2x1c3RlcnMNCmNsdXN0ZXJfc29sdXRpb24gPC0ga21lYW5zKG5vcm1hbGl6ZWRfY2x1c3RlclssMjo0XSwgY2VudGVycyA9IDMpDQoNCiMgU3RvcmUgdGhlIGNsdXN0ZXIgYXNzaWdubWVudHMgYmFjayBpbnRvIHRoZSBjbHVzdGVyaW5nIGRhdGEgZnJhbWUgb2JqZWN0DQpjbHVzdGVyZWRfQmlvcmV0ZW5fdW5saW5lZCRjbHVzdGVyIDwtZmFjdG9yKGNsdXN0ZXJfc29sdXRpb24kY2x1c3RlcikgDQoNCiMgTG9vayBhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGNsdXN0ZXIgYXNzaWdubWVudHMNCnRhYmxlKGNsdXN0ZXJlZF9CaW9yZXRlbl91bmxpbmVkJGNsdXN0ZXIpDQoNCiMgR3JvdXAgYnkgdGhlIGNsdXN0ZXIgYXNzaWdubWVudCBhbmQgY2FsY3VsYXRlIGF2ZXJhZ2VzDQpzeXNfY2x1c19hdmcgPC0gY2x1c3RlcmVkX0Jpb3JldGVuX3VubGluZWQgJT4lDQogICAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lDQogICAgc3VtbWFyaXplX2lmKGlzLm51bWVyaWMsIG1lYW4pDQoNCiMgVmlldyB0aGUgcmVzdWx0aW5nIHRhYmxlDQprYWJsZShzeXNfY2x1c19hdmcpDQoNCiMzIEQgcGxvdA0KcGxvdF9seShjbHVzdGVyZWRfQmlvcmV0ZW5fdW5saW5lZCwgeCA9IH5zeXNfaW1wZXJ2ZGFfZnQyLCB5ID0gfnN5c19yYXdzdG9ybXNpemVtYW5hZ2VkX2luLCB6PSB+bG9hZGluZ19yYXRpbykgJT4lDQogIGFkZF9tYXJrZXJzKGNvbG9yID0gfmNsdXN0ZXIpDQpgYGANCg0KYGBge3Igc2VjdGlvbiAxNSBTdWJzdXJmYWNlIGluZmlsdHJhdGlvbiBjbHVzdGVyaW5nfQ0KDQpjbHVzdGVyZWRfc3Vic3VyZmFjZSA8LSB0YWJsZV9hbGwgJT4lDQogIGZpbHRlcihzeXNfbW9kZWxpbnB1dGNhdGVnb3J5ID09ICJTdWJzdXJmYWNlIGluZmlsdHJhdGlvbiIpICU+JQ0KICBzZWxlY3Qoc3lzdGVtX2lkLCBzeXNfaW1wZXJ2ZGFfZnQyLCBzeXNfcmF3c3Rvcm1zaXplbWFuYWdlZF9pbiwgbG9hZGluZ19yYXRpbywgc3lzX21vZGVsaW5wdXRjYXRlZ29yeSkgJT4lDQogIG5hLm9taXQoKSAlPiUNCiAgZGlzdGluY3QoKQ0KDQojbm9ybWFsemUgdXNpbmcgbWVhbi1zZCBzdGFuZGFyZGl6YXRpb24NCm5vcm1hbGl6ZWRfY2x1c3RlciA8LSBjbHVzdGVyZWRfc3Vic3VyZmFjZQ0Kbm9ybWFsaXplZF9jbHVzdGVyWywyOjRdIDwtIHNjYWxlKGNsdXN0ZXJlZF9zdWJzdXJmYWNlWywyOjRdKQ0KDQojRWxib3cgbWV0aG9kIHNob3dzIGEgc3VidGxlIGVsYm93IGF0IGNsdXN0ZXIgNC1oZW5jZSA0IGNsdXN0ZXJzIGlzIG9wdGltdW0NCiNudW1iZXIgb2YgY2x1c3RlcnMNCmZ2aXpfbmJjbHVzdChub3JtYWxpemVkX2NsdXN0ZXJbLDI6NF0sIGttZWFucywgbWV0aG9kID0gIndzcyIpDQoNCiNrLW1lYW5zIA0KIyBDbHVzdGVyIHVzaW5nIGttZWFucyB3aXRoIGZpdmUgY2x1c3RlcnMNCmNsdXN0ZXJfc29sdXRpb24gPC0ga21lYW5zKG5vcm1hbGl6ZWRfY2x1c3RlclssMjo0XSwgY2VudGVycyA9IDIpDQoNCiMgU3RvcmUgdGhlIGNsdXN0ZXIgYXNzaWdubWVudHMgYmFjayBpbnRvIHRoZSBjbHVzdGVyaW5nIGRhdGEgZnJhbWUgb2JqZWN0DQpjbHVzdGVyZWRfc3Vic3VyZmFjZSRjbHVzdGVyIDwtZmFjdG9yKGNsdXN0ZXJfc29sdXRpb24kY2x1c3RlcikgDQoNCiMgTG9vayBhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGNsdXN0ZXIgYXNzaWdubWVudHMNCnRhYmxlKGNsdXN0ZXJlZF9zdWJzdXJmYWNlJGNsdXN0ZXIpDQoNCiMgR3JvdXAgYnkgdGhlIGNsdXN0ZXIgYXNzaWdubWVudCBhbmQgY2FsY3VsYXRlIGF2ZXJhZ2VzDQpzeXNfY2x1c19hdmcgPC0gY2x1c3RlcmVkX3N1YnN1cmZhY2UgJT4lDQogICAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lDQogICAgc3VtbWFyaXplX2lmKGlzLm51bWVyaWMsIG1lYW4pDQoNCiMgVmlldyB0aGUgcmVzdWx0aW5nIHRhYmxlDQprYWJsZShzeXNfY2x1c19hdmcpDQoNCiMzIEQgcGxvdA0KcGxvdF9seShjbHVzdGVyZWRfc3Vic3VyZmFjZSwgeCA9IH5zeXNfaW1wZXJ2ZGFfZnQyLCB5ID0gfnN5c19yYXdzdG9ybXNpemVtYW5hZ2VkX2luLCB6PSB+bG9hZGluZ19yYXRpbykgJT4lDQogIGFkZF9tYXJrZXJzKGNvbG9yID0gfmNsdXN0ZXIpDQpgYGANCg0KYGBge3Igc2VjdGlvbiAxNiBTdWJzdXJmYWNlIHNsb3cgcmVsZWFzZSAobGluZWQpCSBjbHVzdGVyaW5nfQ0KDQpjbHVzdGVyZWRfc2xvd3JlbF9saW5lZCA8LSB0YWJsZV9hbGwgJT4lDQogIGZpbHRlcihzeXNfbW9kZWxpbnB1dGNhdGVnb3J5ID09ICJTdWJzdXJmYWNlIHNsb3cgcmVsZWFzZSAobGluZWQpIikgJT4lDQogIHNlbGVjdChzeXN0ZW1faWQsIHN5c19pbXBlcnZkYV9mdDIsIHN5c19yYXdzdG9ybXNpemVtYW5hZ2VkX2luLCBzeXNfbW9kZWxpbnB1dGNhdGVnb3J5KSAlPiUNCiAgbmEub21pdCgpICU+JQ0KICBkaXN0aW5jdCgpDQoNCiNub3JtYWx6ZSB1c2luZyBtZWFuLXNkIHN0YW5kYXJkaXphdGlvbg0Kbm9ybWFsaXplZF9jbHVzdGVyIDwtIGNsdXN0ZXJlZF9zbG93cmVsX2xpbmVkDQpub3JtYWxpemVkX2NsdXN0ZXJbLDI6M10gPC0gc2NhbGUoY2x1c3RlcmVkX3Nsb3dyZWxfbGluZWRbLDI6M10pDQoNCiNFbGJvdyBtZXRob2Qgc2hvd3MgYSBzdWJ0bGUgZWxib3cgYXQgY2x1c3RlciA0LWhlbmNlIDQgY2x1c3RlcnMgaXMgb3B0aW11bQ0KI251bWJlciBvZiBjbHVzdGVycw0KZnZpel9uYmNsdXN0KG5vcm1hbGl6ZWRfY2x1c3RlclssMjozXSwga21lYW5zLCBtZXRob2QgPSAid3NzIikNCg0KI2stbWVhbnMgDQojIENsdXN0ZXIgdXNpbmcga21lYW5zIHdpdGggZml2ZSBjbHVzdGVycw0KY2x1c3Rlcl9zb2x1dGlvbiA8LSBrbWVhbnMobm9ybWFsaXplZF9jbHVzdGVyWywyOjNdLCBjZW50ZXJzID0gMikNCg0KIyBTdG9yZSB0aGUgY2x1c3RlciBhc3NpZ25tZW50cyBiYWNrIGludG8gdGhlIGNsdXN0ZXJpbmcgZGF0YSBmcmFtZSBvYmplY3QNCmNsdXN0ZXJlZF9zbG93cmVsX2xpbmVkJGNsdXN0ZXIgPC1mYWN0b3IoY2x1c3Rlcl9zb2x1dGlvbiRjbHVzdGVyKSANCg0KIyBMb29rIGF0IHRoZSBkaXN0cmlidXRpb24gb2YgY2x1c3RlciBhc3NpZ25tZW50cw0KdGFibGUoY2x1c3RlcmVkX3Nsb3dyZWxfbGluZWQkY2x1c3RlcikNCg0KIyBHcm91cCBieSB0aGUgY2x1c3RlciBhc3NpZ25tZW50IGFuZCBjYWxjdWxhdGUgYXZlcmFnZXMNCnN5c19jbHVzX2F2ZyA8LSBjbHVzdGVyZWRfc2xvd3JlbF9saW5lZCAlPiUNCiAgICBncm91cF9ieShjbHVzdGVyKSAlPiUNCiAgICBzdW1tYXJpemVfaWYoaXMubnVtZXJpYywgbWVhbikNCg0KIyBWaWV3IHRoZSByZXN1bHRpbmcgdGFibGUNCmthYmxlKHN5c19jbHVzX2F2ZykNCg0KIzMgRCBwbG90DQpwbG90X2x5KGNsdXN0ZXJlZF9zbG93cmVsX2xpbmVkLCB4ID0gfnN5c19pbXBlcnZkYV9mdDIsIHkgPSB+c3lzX3Jhd3N0b3Jtc2l6ZW1hbmFnZWRfaW4pICU+JQ0KICBhZGRfbWFya2Vycyhjb2xvciA9IH5jbHVzdGVyKQ0KDQoNCmBgYA0KDQpgYGB7ciBzZWN0aW9uIDE3IFN1YnN1cmZhY2Ugc2xvdyByZWxlYXNlICh1bmxpbmVkKQl9DQoNCmNsdXN0ZXJlZF9zbG93cmVsX3VubGluZWQgPC0gdGFibGVfYWxsICU+JQ0KICBmaWx0ZXIoc3lzX21vZGVsaW5wdXRjYXRlZ29yeSA9PSAiU3Vic3VyZmFjZSBzbG93IHJlbGVhc2UgKHVubGluZWQpIikgJT4lDQogIHNlbGVjdChzeXN0ZW1faWQsIHN5c19pbXBlcnZkYV9mdDIsIHN5c19yYXdzdG9ybXNpemVtYW5hZ2VkX2luLCBsb2FkaW5nX3JhdGlvLCBzeXNfbW9kZWxpbnB1dGNhdGVnb3J5KSAlPiUNCiAgbmEub21pdCgpICU+JQ0KICBkaXN0aW5jdCgpDQoNCiNub3JtYWx6ZSB1c2luZyBtZWFuLXNkIHN0YW5kYXJkaXphdGlvbg0Kbm9ybWFsaXplZF9jbHVzdGVyIDwtIGNsdXN0ZXJlZF9zbG93cmVsX3VubGluZWQNCm5vcm1hbGl6ZWRfY2x1c3RlclssMjozXSA8LSBzY2FsZShjbHVzdGVyZWRfc2xvd3JlbF91bmxpbmVkWywyOjNdKQ0KDQojRWxib3cgbWV0aG9kIHNob3dzIGEgc3VidGxlIGVsYm93IGF0IGNsdXN0ZXIgNC1oZW5jZSA0IGNsdXN0ZXJzIGlzIG9wdGltdW0NCiNudW1iZXIgb2YgY2x1c3RlcnMNCmZ2aXpfbmJjbHVzdChub3JtYWxpemVkX2NsdXN0ZXJbLDI6M10sIGttZWFucywgbWV0aG9kID0gIndzcyIpDQoNCiNrLW1lYW5zIA0KIyBDbHVzdGVyIHVzaW5nIGttZWFucyB3aXRoIGZpdmUgY2x1c3RlcnMNCmNsdXN0ZXJfc29sdXRpb24gPC0ga21lYW5zKG5vcm1hbGl6ZWRfY2x1c3RlclssMjozXSwgY2VudGVycyA9IDIpDQoNCiMgU3RvcmUgdGhlIGNsdXN0ZXIgYXNzaWdubWVudHMgYmFjayBpbnRvIHRoZSBjbHVzdGVyaW5nIGRhdGEgZnJhbWUgb2JqZWN0DQpjbHVzdGVyZWRfc2xvd3JlbF91bmxpbmVkJGNsdXN0ZXIgPC1mYWN0b3IoY2x1c3Rlcl9zb2x1dGlvbiRjbHVzdGVyKSANCg0KIyBMb29rIGF0IHRoZSBkaXN0cmlidXRpb24gb2YgY2x1c3RlciBhc3NpZ25tZW50cw0KdGFibGUoY2x1c3RlcmVkX3Nsb3dyZWxfdW5saW5lZCRjbHVzdGVyKQ0KDQojIEdyb3VwIGJ5IHRoZSBjbHVzdGVyIGFzc2lnbm1lbnQgYW5kIGNhbGN1bGF0ZSBhdmVyYWdlcw0Kc3lzX2NsdXNfYXZnIDwtIGNsdXN0ZXJlZF9zbG93cmVsX3VubGluZWQgJT4lDQogICAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lDQogICAgc3VtbWFyaXplX2lmKGlzLm51bWVyaWMsIG1lYW4pDQoNCiMgVmlldyB0aGUgcmVzdWx0aW5nIHRhYmxlDQprYWJsZShzeXNfY2x1c19hdmcpDQoNCiMzIEQgcGxvdA0KcGxvdF9seShjbHVzdGVyZWRfc2xvd3JlbF91bmxpbmVkLCB4ID0gfnN5c19pbXBlcnZkYV9mdDIsIHkgPSB+c3lzX3Jhd3N0b3Jtc2l6ZW1hbmFnZWRfaW4sIHogPSB+bG9hZGluZ19yYXRpbykgJT4lDQogIGFkZF9tYXJrZXJzKGNvbG9yID0gfmNsdXN0ZXIpDQpgYGANCg0KVXBvbiBjbHVzdGVyaW5nIHRoZSBzeXN0ZW1zLCB0aGUgZGF0YSB3aWxsIGJlIGNvbGxlY3RlZCBpbiBhIHVuaWZpZWQgZGF0YSBmcmFtZSBmb3Igc3RyYXRpZmllZCBzYW1wbGluZy4gVGhlIHN0cmF0aWZpZWQgc2FtcGxpbmcgY3JlYXRlcyBhIGJhbGFuY2VkIGxpc3Qgb2Ygc3lzdGVtcyB0aGF0IGFyZSByZWZsZWN0aXZlIG9mIHRoZSBzaXplIG9mIGVhY2ggc21wIHR5cGUgYW5kIHRoYXQgb2YgdGhlIGludGVybmFsIGNsdXN0ZXJzLiBGb3IgZXhhbXBsZSwgdGhlIGNvZGUgYmVsb3cgaGFzIGdlbmVyYXRlZCA1OCBzeXN0ZW0gKFx+IDEwJSBvZiB0b3RhbCB1bm1vbml0b3JlZCBzeXN0ZW1zKTsgdGhlIHNpemUgb2YgZWFjaCBzeXN0ZW0gdHlwZSBpcyBwcm9wb3J0aW9uYWwgdG8gdGhhdCBvZiB0aGUgcG9wdWxhdGlvbiwgYW5kIHdpdGhpbiBlYWNoIHR5cGUgb2Ygc3lzdGVtcywgYSBwcm9wb3J0aW9uYWwgbnVtYmVyIG9mIHN5c3RlbXMgaGFzIGJlZW4gY2hvc2VuIGZyb20gZWFjaCBjbHVzdGVyIGUuZy4sIGluIEJpb2luZmlsdHJhdGlvbiB0eXBlIHN5c3RlbXMsIDcgc3lzdGVtcyBhcmUgcmFuZG9tbHkgY2hvc2VuIGZyb20gY2x1c3RlciAzLCAzIHN5c3RlbXMgZnJvbSBjbHVzdGVyIDIsIGFuZCAyIHN5c3RlbXMgZnJvbSBjbHVzdGVyIDEuDQoNCmBgYHtyIHNlY3Rpb24gMTggc2FtcGxpbmd9DQojYmluZGluZyBhbGwgc3ViLXR5cGUgZ3JvdXBzIGludG8gb25lIHRhYmxlDQpzeXNfY2x1c3RlcmVkX2FsbCA8LSBiaW5kX3Jvd3MoY2x1c3RlcmVkX0Jpb2luZmlsdHJhdGlvblssYygic3lzdGVtX2lkIiwiY2x1c3RlciIsInN5c19tb2RlbGlucHV0Y2F0ZWdvcnkiKV0sIGNsdXN0ZXJlZF9CaW9yZXRlbl9saW5lZFssYygic3lzdGVtX2lkIiwiY2x1c3RlciIsInN5c19tb2RlbGlucHV0Y2F0ZWdvcnkiKV0sIGNsdXN0ZXJlZF9CaW9yZXRlbl91bmxpbmVkWyxjKCJzeXN0ZW1faWQiLCJjbHVzdGVyIiwic3lzX21vZGVsaW5wdXRjYXRlZ29yeSIpXSwgY2x1c3RlcmVkX3Nsb3dyZWxfbGluZWRbLGMoInN5c3RlbV9pZCIsImNsdXN0ZXIiLCJzeXNfbW9kZWxpbnB1dGNhdGVnb3J5IildLCBjbHVzdGVyZWRfc2xvd3JlbF91bmxpbmVkWyxjKCJzeXN0ZW1faWQiLCJjbHVzdGVyIiwic3lzX21vZGVsaW5wdXRjYXRlZ29yeSIpXSwgY2x1c3RlcmVkX3N1YnN1cmZhY2VbLGMoInN5c3RlbV9pZCIsImNsdXN0ZXIiLCJzeXNfbW9kZWxpbnB1dGNhdGVnb3J5IildKQ0KDQojc2FtcGxpbmcgY29kZQ0Kc3RyYXRpZmllZF9zYW1wbGUgPC0gc3lzX2NsdXN0ZXJlZF9hbGwgJT4lDQogICAgZ3JvdXBfYnkoc3lzX21vZGVsaW5wdXRjYXRlZ29yeSwgY2x1c3RlcikgJT4lDQogICAgc2FtcGxlX2ZyYWMoc2l6ZT0wLjEpDQoNCmthYmxlKHN0cmF0aWZpZWRfc2FtcGxlKQ0KYGBgDQo=